package com.cashmama.market.api.application.interceptors;


import com.alibaba.fastjson.JSON;
import com.cashmama.market.api.application.annos.SignIgnore;
import com.cashmama.market.api.application.annos.VisitorAccessible;
import com.cashmama.market.api.application.utils.AppSignUtil;
import com.cashmama.market.api.domain.service.ApiUserCenterService;
import com.cashmama.market.api.domain.vo.RequestLocalInfo;
import com.cashmama.market.api.domain.vo.UserBaseInfo;
import com.cashmama.market.api.domain.vo.UserLoginInfo;
import com.cashmama.market.api.infras.app.RequestBasicInfo;
import com.cashmama.market.api.infras.config.ApolloConfigUtil;
import com.cashmama.market.api.infras.consts.RedisKeyConsts;
import com.cashmama.market.api.infras.enums.PackageNameEnum;
import com.cashmama.market.api.infras.enums.RestCodeEnum;
import com.cashmama.market.api.infras.utils.HttpUtil;
import com.fqgj.base.services.redis.RedisClient;
import com.fqgj.common.utils.JSONUtils;
import com.fqgj.exception.common.ApplicationException;
import com.fqgj.exception.enums.BasicErrorCodeEnum;
import com.fqgj.xjd.user.client.UserBehaviorService;
import com.fqgj.xjd.user.client.enums.PlatformEnum;
import com.fqgj.xjd.user.client.request.UserDailyActivityInfo;
import com.fqgj.xjd.user.client.response.UserAccessInfo;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * Created with IntelliJ IDEA.
 * User: yujianfu (mr.vencnet@gmail.com)
 * Date: 2019-11-25
 * Time: 13:07
 */
@Component
public class AccessInterceptor implements HandlerInterceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(AccessInterceptor.class);

    public static final Set<String> ALLOWED_PATHS = new HashSet<>(
            Arrays.asList("/ok")
    );

    @Autowired
    private ApolloConfigUtil apolloConfigUtil;

    @Autowired
    private ApiUserCenterService apiUserCenterService;

    @Autowired
    private UserBehaviorService userBehaviorService;

    @Autowired
    private RedisClient redisClient;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {

        String requestURI = request.getRequestURI();
        for (String s : ALLOWED_PATHS) {
            if (requestURI.equals(s)) {
                LOGGER.info("拦截器放行,url:{}", requestURI);
                return true;
            }
        }

        boolean testUser = false;
//        try {
//            String mobile_imei = JSON.parseObject(request.getHeader("basicParams")).getString("mobile_imei");
//            if ("454768211103993".equals(mobile_imei) || "866875033410240".equals(mobile_imei)) {
//                LOGGER.info("测试用户放行,url:{}", requestURI);
//                testUser = true;
//            }
//        } catch (Exception e) {
//        }

        Long startTime = System.currentTimeMillis();
        MDC.put("startTime", startTime.toString());

        if (!testUser) {
            Boolean switchStatus = apolloConfigUtil.getApiSwitch();
            if (!switchStatus) {
                String switchMsg = apolloConfigUtil.getApiSwitchMsg();
                throw new ApplicationException(switchMsg);
            }
        }

        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type,Accept,X-Requested-With,remember-me,bid,basicParams");

        RequestLocalInfo.clearThreadLocal();
        if (isWebResources(request)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) o;
        SignIgnore signIgnoreAnnotation = handlerMethod.getMethodAnnotation(SignIgnore.class);
        if (signIgnoreAnnotation != null) {
            return true;
        }

        verifySignInfo(request);
        RequestBasicInfo basicVO = RequestLocalInfo.getRequestBasicInfo();
        verifyToken(basicVO.getToken(), handlerMethod, request);

        return true;
    }


    private void verifyToken(String accessToken, HandlerMethod handlerMethod, HttpServletRequest request) {
        //获取不要校验token的注解
        VisitorAccessible annotation = handlerMethod.getMethodAnnotation(VisitorAccessible.class);
        if (annotation != null && StringUtils.isEmpty(accessToken)) {
            return;
        }

        if (StringUtils.isBlank(accessToken)) {
            throw new ApplicationException(RestCodeEnum.NEED_TOKEN_ERROR);
        }

        if (!hasPermission(accessToken) && annotation == null) {
            LOGGER.error("User authentication failed");
            throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
        }
        //保存用户日活数据
        saveUserDailyData(RequestLocalInfo.getRequestBasicInfo(), RequestLocalInfo.getCurrentUser(), HttpUtil.getIpAddr(request));
    }

    private void verifySignInfo(HttpServletRequest request) throws Exception {
        String basicParams = request.getHeader("basicParams");

        if (StringUtils.isBlank(basicParams)) {
            throw new ApplicationException(RestCodeEnum.SYSTEM_PARAMETER_ERROR);
        }


        RequestBasicInfo basicVO = JSONUtils.json2pojo(basicParams, RequestBasicInfo.class);
        boolean isH5 = RequestBasicInfo.wechatUser(basicVO.getChannel());
        if (!isH5 && basicVO.getVersionCode() < 1 && !request.getRequestURI().contains("/home")) {
            throw new ApplicationException(RestCodeEnum.APP_VERSION_EXPIRED);
        }
        boolean needVerifyAppSign = !isH5 && !request.getRequestURI().contains("appcode/get") && !request.getRequestURI().contains("assistance/submit");

        if (needVerifyAppSign && !AppSignUtil.verifyAppSign(basicVO, "", apolloConfigUtil.isServerTest())) {
            throw new ApplicationException(RestCodeEnum.PARAMETER_SIGN_ERROR);
        }

        RequestLocalInfo.putCurrentEnvIsTest(apolloConfigUtil.isServerTest());
        RequestLocalInfo.putCurrentRequestBasicVO(basicVO);
    }

    private boolean isWebResources(HttpServletRequest request) {

        String uri = request.getRequestURI();
        uri = uri.replace("//", "/");
        if (uri.startsWith("/web/") || uri.contains("swagger") || uri.contains("api-docs")) {
            return true;
        }

        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o,
                           ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                                Object handler, Exception e) throws Exception {

        try {
            String url = httpServletRequest.getRequestURI();
            Long startTime = Long.parseLong(MDC.get("startTime"));
            Long costTime = System.currentTimeMillis() - startTime;

            //todo 增加访问历史日志记录
        } catch (Exception ex) {
            LOGGER.error("afterCompletion error:" + ex);
        }
    }

    public AccessInterceptor() {
    }

    private Boolean hasPermission(String accessToken) {
        UserLoginInfo userLoginInfo = apiUserCenterService.getUserLoginInfoByToken(accessToken);

        if (userLoginInfo == null) {
            return false;
        }

        UserBaseInfo userBaseInfoByUserCode = apiUserCenterService.getUserBaseInfoByUserCode(userLoginInfo.getUserCode());
        if (userBaseInfoByUserCode == null) {
            LOGGER.error("登陆的user_code不存在", accessToken, userLoginInfo.getUserCode());
            throw new ApplicationException(RestCodeEnum.TOKEN_NOT_AVAILABLE);
        }

        UserAccessInfo userAccessInfo = new UserAccessInfo(userLoginInfo.getUserCode(), accessToken, userBaseInfoByUserCode.getMobile());
        RequestLocalInfo.putCurrentUser(userAccessInfo);

        return true;
    }

    private String getRequestBody(HttpServletRequest request) {

        String businessParams = "";

        try {
            InputStream inputStream = request.getInputStream();
            businessParams = IOUtils.toString(inputStream, Charset.defaultCharset());
            if (StringUtils.isNotEmpty(businessParams)) {
                return businessParams;
            }
        } catch (Exception e) {
            throw new ApplicationException(BasicErrorCodeEnum.PARAM_RESOLVE_ERROR);
        }


        return businessParams;

    }

    /**
     * 保存用户日活数据
     */
    private void saveUserDailyData(RequestBasicInfo requestBasicInfo, UserAccessInfo userAccessInfo, String ip) {
        if (userAccessInfo == null) {
            return;
        }
        try {
            if (redisClient.setnx(RedisKeyConsts.USER_DAILY_DATA_LOCK, userAccessInfo.getToken(), userAccessInfo.getUserCode())) {
                UserDailyActivityInfo userDailyActivityInfo = new UserDailyActivityInfo();
                userDailyActivityInfo.setUserCode(userAccessInfo.getUserCode());
                userDailyActivityInfo.setAppCode(requestBasicInfo.getAppCode());
                userDailyActivityInfo.setPlatform(PlatformEnum.getEnumByDesc(requestBasicInfo.getAppClient()).getType());
                userDailyActivityInfo.setAppVersion(requestBasicInfo.getAppVersion());
                userDailyActivityInfo.setVersionCode(requestBasicInfo.getVersionCode() + "");
                userDailyActivityInfo.setIp(ip);
                userDailyActivityInfo.setPackageName(StringUtils.isEmpty(requestBasicInfo.getPackageName()) ? PackageNameEnum.DHANADHAN.getPackageName() : requestBasicInfo.getPackageName());
                userBehaviorService.insertUserDailyActivityInfo(userDailyActivityInfo);
                Long now = System.currentTimeMillis();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                Long overTime = 86400 - (now - (sdf.parse(sdf.format(now)).getTime())) / 1000;
                redisClient.expire(RedisKeyConsts.USER_DAILY_DATA_LOCK, overTime.intValue(), userAccessInfo.getUserCode());
            }
        } catch (Exception e) {
            LOGGER.error("================ save daily data error e:{} ===============", e);
        }
    }
}


